# -*- coding: utf-8 -*-
# standard
import os
import sys

# third
from fastapi import APIRouter
from fastapi import Depends
from fastapi import Request
from fastapi import Response
from fastapi import Header
from fastapi import Query as QueryParam
from fastapi import Path as PathParam
from fastapi.exceptions import HTTPException
# local
_P_PATH =  os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
if _P_PATH not in sys.path:
    sys.path.append(_P_PATH)
from functions import *
from toolbox.common_tools import INCLUDE_SCHEMA
from toolbox.common_tools import get_fastapi_real_ip


"""
@Title:   
@File: iot_view.py
@Author: walle 2024年10日10日 17时03分35秒
@Version: 1.0.0
@Desc: 
"""


router = APIRouter()


async def check_data_authorization(request: Request, authorization: str | None = Header(None, description="请求头")) -> Optional[str]:
    """
    检查iot设备上传数据时授权 失败会抛出异常，否则会返回请求头或者None(没有请求头的情况)
    注意，这个函数在不同的视图的实现不同
    :return: _description_
    """
    if authorization != DATA_AUTHORIZATION and request.url.hostname != "127.0.0.1":
        raise HTTPException(status_code=404)
    return authorization


async def check_alert_authorization(request: Request, authorization: str | None = Header(None, description="请求头")) -> Optional[str]:
    """
    检查influxdb发送警告时授权 失败会抛出异常，否则会返回请求头或者None(没有请求头的情况)
    注意，这个函数在不同的视图的实现不同
    :return: _description_
    """
    if authorization != ALERT_AUTHORIZATION and request.url.hostname != "127.0.0.1":
        raise HTTPException(status_code=404)
    return authorization


async def check_get_authorization(request: Request, authorization: str | None = Header(None, description="请求头")) -> Optional[str]:
    """
    检查微服务发送get请求时授权 失败会抛出异常，否则会返回请求头或者None(没有请求头的情况)
    注意，这个函数在不同的视图的实现不同
    :return: _description_
    """
    if authorization != GET_AUTHORIZATION and request.url.hostname != "127.0.0.1":
        raise HTTPException(status_code=404)
    return authorization


################## 数据采集 ########################


@router.post("/temperature/{sensor_brand}", tags=['数据采集'], summary="接收温度传感器数据 旧版 仅限麦上包子控江店", response_class=Response, include_in_schema=INCLUDE_SCHEMA)
async def temperature_view(
    request: Request,
    sensor_brand: str = PathParam(..., description="传感器品牌, 不参与写入，但是和数据格式有关"),
    ):
    """
    /iot/sensor/temperature/wn1010
    接收温度传感器数据 目前指定是麦上包子控江店，其他门店的传感器数据不接收
    :param brand: 传感器品牌
    """
    ignore_auth_uuid = '30FFCF053054483213600343'  # 没有 authorization 的温度传感器iot主机
    json_data = {}
    try:
        json_data = await request.json()
    except:
        pass
    uuid = json_data.get('uuid')
    resp = "ok"
    if uuid == ignore_auth_uuid:  # 麦上包子控江店的 主机没有配置 authorization
        customer_id, store_id = DEFAULT_CUSTOMER_ID, DEFAULT_STORE_ID  # todo: 麦上包子控江店的 写入，等待修改过门店的传感器接收器主机的设置再说   
        logger.info(f"接收到{sensor_brand}温度传感器数据：json_data: {json_data}")
    else:
        logger.info(f"接收到未授权的{sensor_brand}温度传感器数据：json_data: {json_data}")    
        resp = "err"
    
    if resp == "ok" and customer_id and store_id:
        try:
            await save_sensor_data(data_type="temperature", 
                            customer_id=customer_id,
                            store_id=store_id,
                            uuid=uuid,
                            grp=json_data['grp'],
                            nodes=json_data['nodes'])
        except Exception as e:
            logger.error(f"保存数据失败：{e}")
            resp = "error"
        finally:
            pass
    else:
        pass
    return resp


@router.post("/sensor/temperature/{customer_id}/{store_id}", tags=['数据采集'], summary="接收温度传感器数据 新版", response_class=Response, include_in_schema=INCLUDE_SCHEMA)
async def sensor_temperature_view(
    request: Request,
    customer_id: int = PathParam(..., description="品牌客户id"),
    store_id: int = PathParam(..., description="门店id"),
    authorization: str | None = Depends(check_data_authorization)
    ):
    """
    接收沈阳维恩的温度传感器数据
    """
    json_data = {}
    try:
        json_data = await request.json()
    except:
        pass
    uuid = json_data.get('uuid')
    resp = "ok"
    if authorization:  # 除去麦上包子控江店的，其他店的都要配置 authrization
        logger.info(f"接收到品牌客户({customer_id})的门店({store_id})温度传感器数据json_data: {json_data}")
    else:
        logger.info(f"接收到未授权的带请求头温度传感器数据：customer_id: {customer_id}, sttore: {store_id},authorization: {authorization}, json_data: {json_data}")    
        resp = "err"
    
    if resp == "ok" and customer_id and store_id:
        try:
            await save_sensor_data(data_type="temperature",
                                   customer_id=customer_id,
                                   store_id=store_id,
                                   uuid=uuid,
                                   grp=json_data['grp'],
                                   nodes=json_data['nodes'])
        except Exception as e:
            logger.error(f"保存数据失败：{e}")
            resp = "error"
        finally:
            pass
    else:
        pass
    return resp


################## 警告采集 ########################
    

@router.post("/threshold_alert/temperature", tags=['数据采集'], summary="温度传感器超过报警", response_class=Response, include_in_schema=INCLUDE_SCHEMA)
async def temperature_alert_view(
    request: Request,
    authorization: str | None = Depends(check_alert_authorization)
    ):
    """
    接收温度传感器温度超过设定阈值的警告
    :param brand: 传感器品牌
    """
    json_data = {}
    try:
        json_data = await request.json()
    except:
        pass
    logger.info(f"收到温度超阈值报警：json_data: {json_data}")
    resp = "ok"
    json_data['type'] = SensorEventType.THRESHOLD.value
    TemperatureSensorEventOrm.add_one(**json_data)
    return resp
    

@router.post("/offline_alert/temperature", tags=['数据采集'], summary="温度传感器离线报警", response_class=Response, include_in_schema=INCLUDE_SCHEMA)
async def offline_alert_view(
    request: Request,
    authorization: str | None = Depends(check_alert_authorization)
    ):
    """
    接收温度传感器离线警告
    :param brand: 传感器品牌
    """
    json_data = {}
    try:
        json_data = await request.json()
    except:
        pass

    logger.info(f"收到温度传感器离线报警：json_data: {json_data}")
    resp = "ok"
    json_data['type'] = SensorEventType.OFFLINE.value
    TemperatureSensorEventOrm.add_one(**json_data)
    return resp


################## 信息查询 ########################
   

@router.get("/sensor/customer/count", tags=['数据查询'], summary="以统计传感器数量", response_model=Resp, include_in_schema=INCLUDE_SCHEMA)
async def get_sensor_customer_count_view(
    authorization: str | None = Depends(check_get_authorization),
    customer_id: int | None = QueryParam(None, description="品牌id"),
    ):
    return get_sensor_customer_count(customer_id=customer_id)


@router.get("/sensor_alert/customer/summary", tags=['数据查询'], summary="以品牌客户为key, 门店为分组的 influxdb报警统计", response_model=Resp, include_in_schema=INCLUDE_SCHEMA)
async def get_sensor_alert_customer_summary_view(
    authorization: str | None = Depends(check_get_authorization),
    customer_id: int | None = QueryParam(None, description="品牌id"),
    sensor_category: str = QueryParam(..., description="传感器类型，温度传感器/电量传感器"),
    ):
    return get_sensor_alert_customer_summary_group_by_store(customer_id=customer_id, sensor_category=sensor_category)


class AlertCountByPriorityParams(BaseModel):
    customer_ids: List[int] | None = Field(None, description="品牌id")
    store_ids: List[int] | None = Field(None, description="门店id")
    days: int = Field(3, description="统计天数")
    not_recovery: int | None = Field(None, description="1 仅统计未恢复的数据 0 仅统计已恢复的数据 None 全部")

@router.post("/alert/count/group_by_priority", tags=['数据查询'], summary="以优先级分组的报警统计", response_model=Resp, include_in_schema=INCLUDE_SCHEMA)
async def get_alert_count_by_priority_view(
    params: AlertCountByPriorityParams,
    authorization: str | None = Depends(check_get_authorization),
    ):
    return alert_summary_group_by_priority_sync(**params.model_dump())



class InfluxdbAlertPageParams(BaseModel):
    customer_ids: List[int] | None = Field(None, description="品牌id")
    store_ids: List[int] | None = Field(None, description="门店id")
    not_recovery: int | None = Field(None, description="1 仅统计未恢复的数据 0 仅统计已恢复的数据 None 全部")
    uuid: str | None = Field(None, description="传感器主机 uuid")
    group_id: int | None = Field(None, description="传感器组id")
    sensor_category: str | None = Field(None, description="传感器类型")
    sensor_id: int | None = Field(None, description="传感器id")
    is_current_month: int = Field(0, description="是否查询本月数据")
    priority: str | None = Field(None, description="优先级 预警/告警")
    start: datetime | None = Field(None, description="警告发生的开始时间")
    end: datetime | None = Field(None, description="警告发生的结束时间")
    type: str | None = Field(None, description="警告类型，三种选择(超过阈值/设备离线/None)")
    status: int | None = Field(None, description="状态，三种选择 1.异常中， 0.已恢复, None.全部")
    limit: int | None = Field(None, description="输出限制")
    page_num: int = Field(1, description="页码")
    page_size: int = Field(10, description="每页多少数据")


@router.post("/influxdb_alert/page", tags=['数据查询'], summary="分页查看influxdb报警统计", response_model=Resp, include_in_schema=INCLUDE_SCHEMA)
async def page_influxdb_alert_view(
    params: InfluxdbAlertPageParams,
    authorization: str | None = Depends(check_get_authorization),
    ):
    resp = page_influxdb_alerts(**params.model_dump())
    return resp


@router.get("/temperature_sensor/get_id", tags=['数据查询'], summary="通过uuid,group_id和传感器sn获取传感器id", response_model=Resp, include_in_schema=INCLUDE_SCHEMA)
async def get_sensor_id_by_other_view(
    authorization: str | None = Depends(check_get_authorization),
    uuid: str = QueryParam(..., description="传感器主机uuid"),
    group_id: int = QueryParam(..., description="传感器组id"),
    sensor_sn: str = QueryParam(..., description="传感器sn"),
):
    db_session, resp = new_db_session(), Resp()
    s = select(TemperatureSensorOrm.id).where(and_(
        TemperatureSensorOrm.uuid == uuid,
        TemperatureSensorOrm.group_id == group_id,
        TemperatureSensorOrm.sensor_sn == sensor_sn,
        TemperatureSensorOrm.is_deleted == 0,
    ))
    sensor_id = db_session.scalar(s)
    if sensor_id is None:
        resp.message = "没有找到对应的传感器id"
    else:
        resp.data = sensor_id   
    db_session.close()
    return resp


@router.get("/sensor_info/get_by_id", tags=['数据查询'], summary="使用id查看温度传感器", response_model=Resp, include_in_schema=INCLUDE_SCHEMA)
async def get_sensor_by_id_view(
    authorization: str | None = Depends(check_get_authorization),
    sensor_id: int = QueryParam(..., description="传感器id"),
):
    db_session, resp = new_db_session(), Resp()
    sensor = db_session.get(SensorOrm, sensor_id)
    if sensor is None or sensor.is_deleted == 1:
        resp.message = f"无效的传感器ID:{sensor_id}"
    else:
        resp.data = sensor.to_dict(to_plain=True)
    db_session.close()
    return resp


class GetSensorByIdsParams(BaseModel):
    sensor_ids: List[int] = Field(..., description="传感器id的数组")
    sensor_category: str | None = Field(None, description="传感器类型")
    customer_id: int | None = Field(None, description="品牌客户id")
    store_id: int | None = Field(None, description="门店ID")


@router.post("/sensor/get_by_ids", tags=['数据查询'], summary="使用id查看多个温度/电量传感器的信息", response_model=Resp, include_in_schema=INCLUDE_SCHEMA)
async def get_sensor_by_id_view(
    params: GetSensorByIdsParams,
    authorization: str | None = Depends(check_get_authorization),
):
    """
    当 sensor_ids 为空 store_id 不为空时，查询 门店全部的传感器
    """
    resp = Resp()
    if not params.sensor_ids and not params.store_id and not params.customer_id:
        resp.message = f"至少需要一个过滤参数"
    else:
        db_session = new_db_session()
        s = select(SensorOrm)
        if params.sensor_ids:
            s = s.where(SensorOrm.id.in_(params.sensor_ids))
        else:
            s = s.where(SensorOrm.store_id == params.store_id)
        if params.sensor_category:
            s = s.where(SensorOrm.sensor_category == params.sensor_category)
        if params.customer_id:
            s = s.where(SensorOrm.customer_id == params.customer_id)
        s = s.where(SensorOrm.is_deleted == 0)
        sensors = [x.to_dict(to_plain=True) for x in db_session.scalars(s).all()]
        resp.data = sensors
        db_session.close()
    return resp


@router.get("/temperature_sensor/page", tags=['数据查询'], summary="分页查看温度传感器", response_model=Resp, include_in_schema=INCLUDE_SCHEMA)
async def page_temperature_sensor_view(
    authorization: str | None = Depends(check_get_authorization),
    keyword: str | None = QueryParam(None, description="关键字"),
    customer_id: int | None = QueryParam(None, description="品牌id"),
    store_id: int | None = QueryParam(None, description="门店id"),
    uuid: str | None = QueryParam(None, description="uuid"),
    group_id: int | None = QueryParam(None, description="组id"),
    page_num: int = QueryParam(1, description="页码"),
    page_size: int = QueryParam(10, description="每页多少数据"),
):
    return page_temperature_sensors(keyword=keyword,
                                    customer_id=customer_id,
                                    store_id=store_id,
                                    uuid=uuid,
                                    group_id=group_id,
                                    page_num=page_num,
                                    page_size=page_size)


@router.get("/electricity_sensor/page", tags=['数据查询'], summary="分页查看电量传感器", response_model=Resp, include_in_schema=INCLUDE_SCHEMA)
async def page_electricity_sensor_view(
    authorization: str | None = Depends(check_get_authorization),
    keyword: str | None = QueryParam(None, description="关键字"),
    customer_id: int | None = QueryParam(None, description="品牌id"),
    store_id: int | None = QueryParam(None, description="门店id"),
    uuid: str | None = QueryParam(None, description="uuid"),
    group_id: int | None = QueryParam(None, description="组id"),
    has_category: int = QueryParam(0, description="是否有分类信息"),
    ignore_main: int = QueryParam(0, description="是否忽略主传感器"),
    page_num: int = QueryParam(1, description="页码"),
    page_size: int = QueryParam(10, description="每页多少数据"),
):
    return page_electricity_sensors(keyword=keyword,
                                    customer_id=customer_id,
                                    store_id=store_id,
                                    uuid=uuid,
                                    group_id=group_id,
                                    ignore_main=ignore_main,
                                    has_category=has_category,
                                    page_num=page_num,
                                    page_size=page_size)


class GetElectricitySensorMainParams(BaseModel):
    customer_ids: List[int] | None = Field(None, description="品牌用户id列表")
    store_ids: List[int] | None = Field(None, description="门店id列表")


@router.post("/electricity_sensor/get_main", tags=['数据查询'], summary="获取主电量传感器信息", response_model=Resp, include_in_schema=INCLUDE_SCHEMA)
async def get_electricity_sensor_main_view(
    params: GetElectricitySensorMainParams,
    authorization: str | None = Depends(check_get_authorization),
):
    return get_main_electricity_sensors(**params.model_dump())


@router.get("/sensor/status/get_by_id", tags=['数据查询'], summary="通过id查看传感器状态", response_model=Resp, include_in_schema=INCLUDE_SCHEMA)
async def get_sensor_status_by_id_view(
    sensor_id: int,
    authorization: str | None = Depends(check_get_authorization),
):
    # todo： 目前没用 2025-3-4
    return get_sensor_status_by_id(sensor_id)


@router.get("/sensor/offline/count", tags=['数据查询'], summary="统计传感器在线离线数量", response_model=Resp, include_in_schema=INCLUDE_SCHEMA)
async def count_sensor_simple_view(
    customer_id: int | None = QueryParam(None, description="品牌id"),
    store_id: int | None = QueryParam(None, description="门店id"),
    authorization: str | None = Depends(check_get_authorization),
):
    return count_sensor_simple(customer_id=customer_id, store_id=store_id)


class SensorCategoryStatusCountParams(BaseModel):
    customer_ids: List[int] | None = Field(None, description="品牌id")
    store_ids: List[int] | None = Field(None, description="门店id")


@router.post("/sensor/category/status/count", tags=['数据查询'], summary="统计不同类型传感器不同状态的数量", response_model=Resp, include_in_schema=INCLUDE_SCHEMA)
async def count_sensor_category_status_view(
    params: SensorCategoryStatusCountParams,
    authorization: str | None = Depends(check_get_authorization),
):
    return count_sensor_category_status(**params.model_dump())


class SensorAlertCountParams(BaseModel):
    customer_ids: List[int] | None = Field(None, description="品牌id")
    store_id: int | None = Field(None, description="门店id")
    ignore_store_ids: List[int] | None = Field(None, description="忽略门店id")
    days: int = Field(30, description="统计天数 截至到之前多少天")


@router.post("/sensor/alert/count", tags=['数据查询'], summary="统计传感器报警数量", response_model=Resp, include_in_schema=INCLUDE_SCHEMA)
async def count_sensor_alert_view(
    params: SensorAlertCountParams,
    authorization: str | None = Depends(check_get_authorization),
):
    return count_sensor_alert_quantity(**params.model_dump())


if __name__ == '__main__':
    pass